/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package chameleon.model.formula.parser;

import chameleon.model.formula.Tag;
import java.io.IOException;

/**
 * This class implements a parser for mathematical expresions that contains
 * the following operator : 
 * +,*,/, - (unary and binary);
 *
 * The syntax is the following : 
 * 
 * f :: digit | f + f | -f | (f) |
 *
 * @author simon
 */
public class MathematicalParser extends AbstractParser {

    public MathematicalParser(String input){
        super();
        lineRead = input;
    }

    private Word newWord(String prop){
        char[] proposition = prop.toCharArray();
        for (int i=0; i < proposition.length; ++i ){
            int nbre = proposition[i]-'0';
            if (!(nbre >=0 && nbre <= 9))
                return new Word(prop,Tag.PROPOSITION);
        }
        return new Word(prop,Tag.INTEGER);
    }

    /**
     * Return the nex token.
     *
     * @return
     * @throws IOException
     */

    @Override
    protected Word nextToken() throws IOException {
        String prop ="";
        char[] line = lineRead.toCharArray();
        char charRead;
        boolean haveProp = false;
        while (lineRead !=null ){

            if (pos_ == line.length){
                if (haveProp){
                    haveProp = false;
                    return newWord(prop);
                }
                /*if((lineRead = br.readLine()) != null){
                    line = lineRead.toCharArray();
                    pos_ = 0;
                */
                else
                    return new Word (Tag.S_END,Tag.END);
            }

            charRead = line[pos_];
            pos_++;

            if (charRead == '+'){
                if (!haveProp){
                    return new Word(Tag.S_PLUS,Tag.PLUS);
                }
                else{
                    pos_--;
                    return newWord(prop);
                }
            }
            else if(charRead == '-'){
                if (!haveProp)
                    return new Word (Tag.S_MINUS,Tag.MINUS);
                else{
                    pos_--;
                    return newWord(prop);
                }
            }
             else if(charRead == '*'){
                if (!haveProp)
                    return new Word (Tag.S_MUL,Tag.MUL);
                else{
                    pos_--;
                    return newWord(prop);
                }
            }
             else if(charRead == '/'){
                if (!haveProp)
                    return new Word (Tag.S_DIVIDE,Tag.DIVIDE);
                else{
                    pos_--;
                    return newWord(prop);
                }
            }
            else if (charRead == '('){
                return new Word(Tag.S_OPEN_PARENTHESE,Tag.OPEN_PARENTHESE);
            }
            else if (charRead == '='){
                if (!haveProp)
                    return new Word(Tag.S_ASSIGN,Tag.ASSIGN);
                else{
                    pos_--;
                    return newWord(prop);
                }
            }

            else if (charRead == ')'){
                if (!haveProp)
                    return new Word(Tag.S_CLOSE_PARENTHESE,Tag.CLOSE_PARENTHESE);
                else{
                    pos_--;
                    return newWord(prop);
                }
            }
            else if (Character.isWhitespace(charRead)){
                if (haveProp)
                    return newWord(prop);
            }
            /*else if (charRead == '\n'){
                lineRead = br.readLine();
            }*/
            else{
                //We have a proposition
                prop += charRead;
                haveProp = true;
            }
        }
        if (haveProp)
            return new Word(prop, Tag.PROPOSITION);
        return new Word(Tag.S_END,Tag.END);
    }


     /**
     * The following methods are the description of the grammar.
     */

    @Override
    protected void FORMULA() throws IOException {
        EXPR();
        ASSIGN();
    }
    
    private void ASSIGN() throws IOException{
        if (match(Tag.ASSIGN)){
            push(true);
            EXPR();
            ASSIGN();
            createFormulaOnStack();
        }
        else
            ;//End of recursion
    }

    private void EXPR() throws IOException{
        TERM();
        EXPRP();
    }

    private void EXPRP() throws IOException{
        if (match(Tag.PLUS)){
            push(true);
            TERM();
            EXPRP();
            createFormulaOnStack();
        }
        else if(match(Tag.MINUS)){
            push(true);
            TERM();
            EXPRP();
            createFormulaOnStack();
        }
        else
            ;//End of recursion
    }

    private void TERM() throws IOException{
        UNARY();
        TERMP();
    }

    private void TERMP() throws IOException{
        if (match(Tag.MUL)){
            push(true);
            UNARY();
            TERMP();
            createFormulaOnStack();
        }
        else if(match(Tag.DIVIDE)){
            push(true);
            UNARY();
            TERMP();
            createFormulaOnStack();
        }
        else
            ;//End of recursion
    }

    private void UNARY() throws IOException{
        if(match(Tag.MINUS)){
            token_.setTag(Tag.MINUS_UNARY);
            token_.setWord(Tag.S_MINUS_UNARY);
            push(true);
            FORMULA();
            createFormulaOnStack();
        }
        else
            FACTOR();
    }

    private void FACTOR() throws IOException{
        if (match(Tag.OPEN_PARENTHESE)){
            token_ = nextToken();
            FORMULA();
            if (!match(Tag.CLOSE_PARENTHESE))
                throw new IOException("Missing closed parenthese");
            token_ = nextToken();
        }
        else if (match(Tag.PROPOSITION) || match(Tag.INTEGER)){
            push(false);
        }
    }

}
